#include "threadpool.h"
#include "log.h"


namespace evm { namespace common {

ThreadPool::ThreadPool(size_t pool_size, size_t queue_max_size)
        : queue_(queue_max_size), pool_size_(pool_size) {
    if (pool_size_ == 0)
        throw invalid_argument("Thread pool size must be > 0");
}

bool ThreadPool::post(Task task, void* data, bool block) {
    return queue_.put(make_pair(task, data), block);
}

void ThreadPool::start() {
    if (!stopped_)  // has started
        return;

    stopped_ = false;

    if (joinable())
        join();

    for (size_t i = 0; i < pool_size_; ++i) {
        threads_.emplace_back([this]() {
            while (!stopped_) {
                Task task;
                void* data;
                /* Block only 1 second, to get chance for checking stop instruction. */
                std::tie(task, data) = queue_.get(true, 1);

                try {
                    /* Task handling.
                     * If task handling blocked, this thread will be in busy state
                     * and do not grab next task. */
                    if (task) {
                        queue_.task_done();
                        task(data);
                    }
                }
                catch (const exception &e) {
                    log_error("Thread pool task handling error: {}", e.what());
                }
            }
        });
    }
}

void ThreadPool::stop(bool wait) {
    if (wait)
        queue_.join();
    stopped_ = true;
}

void ThreadPool::join() {
    for (auto& t: threads_)
        t.join();
    threads_.clear();
    queue_.clear();
}

bool ThreadPool::joinable() {
    return !threads_.empty();
}

size_t ThreadPool::tasks() {
    return queue_.size();
}

}}
